C++ on MSVC講習/変数基礎
あらすじと概要
前回は、Hello World!プログラムを通じて、基本的なコードの決まりを解説しました。
また、標準出力やリテラルを使い、コンソールへ単純な文字列を出力できるようになりました。
今回は、実行時に値を保持できるようになり、入力が出来るようになります。
重要語
変数
名前に値を関連付けることで値を保存する機能
型
コードでの振る舞いを規定し、実行前にミスを検出する機能
組み込み型
C++に元から組み込まれている型
ユーザー定義型
ライブラリ等で定義を導入できる型
宣言
コンパイラに新たな識別子を使うことを伝える構文
識別子
変数などの名前
キーワード
C++規格で特別な意味を持たせられたトークン
初期化
宣言と同時に値を設定する文法
代入
宣言後に値を変更する方法
標準入力
標準で用意されている入力元
std::cin
プログラム実行時に標準入力に紐づけられるもの
符号化
データをコンピューターで表わせる形式に変換すること
文字コード
文字に割り当てる番号と、それの符号化形式を定めるもの
必要語
コンソール
文字のみでPCとやり取りするCUI形式の画面
式
演算子とオペランド(被演算子)の並び
演算子
+
や-
など、何らかの演算を表す記号
オペランド
何らかの「値」
文
順番に実行されるC++プログラムの断片
トークン
コード上で意味を持つ最小の単位
標準出力
標準で用意されている出力先
std::cout
プログラム実行時に標準出力に紐づけられるもの
リテラル
コード上に直接書かれているデータ
C++の変数
変数って、何なんでしょうか。ひとまず解説するので一読してみてください。
一読したら、進んでみて、また分からなくなったら読んでみてくださいね。
リテラルだと不便なところ
前回のコードHello World!では、リテラルを使っていました。
それでは、同じ値でも複数の文で使用するには、その度に書かなくてはいけません。
他にも、実行時に値を決定したり、変更したりすることが出来ません。
前回のプログラムだと、表示する内容を変えるごとにコンパイルしないといけません。
値を保存すればいいのよ
それらは、実行時に値を保存・変更できる仕組みがあれば解決することが出来ます。
その仕組みが変数で、名前に値を関連付けることで値を保存することが出来るようになります。
型
C++の変数は、必ず型を持っていて、型によってコードでの振る舞いが制限されています。
そして、型による振る舞いの制限が守られているかコンパイル時にチェックされます。
それによって、プログラマーのミスを実行前に発見し、修正することが出来るのです。
ですから多くの場合、型には、意味論としての使用法や性質が名前として付けられます。
型の種類
ライブラリ等が必要ない、C++に元から組み込まれている型を組み込み型と総称します。
一方、ライブラリ等で型の定義を導入できるものをユーザー定義型と総称します。
今回は組み込み型から、整数だけを扱えるint
と、小数まで扱えるdouble
を、
ユーザー定義型からは、文字列を扱う為のstd::string
について解説します。
整数を扱おう
まずは整数を扱ってみましょう。次のコードをコピペして実行しましょう。
整数
#include <iostream>
int main()
{
//符号あり(負数が使える)
signed short int a;
signed int b = 10;
signed long int c(20);
signed long long int d{ 40 };
// 初期化されていない「a」は、値が不定なので使用してはいけない
a = 1; // 「a」に1を代入
// 代入された「a」は、代入された値になったので、使用してよい
std::cout << "a : " << a << "\n"
<< "b : " << b << "\n"
<< "c : " << c << "\n"
<< "d : " << d << "\n\n";
// 符号なし(負数が使えない)
unsigned u_a = 1, u_b(10), u_c{ 20 }, u_d = { 40 };
std::cout << "u_a : " << u_a << "\n"
<< "u_b : " << u_b << "\n"
<< "u_c : " << u_c << "\n"
<< "u_d : " << u_d << "\n";
}
実行結果
a : -1
b : -10
c : -20
d : -40
<
u_a : 1
u_b : 10
u_c : 20
u_d : 40
解説
さて、コードの解説に入っていきます。
宣言
変数は「名前に値を関連付けることで値を保存」出来ると説明しました。
そのためには、まずコンパイラに変数を使うことを教える必要があります。
それを宣言(文)といい、必要な所を単純化すると、以下のような構文になります。
宣言子では、識別子 初期化子(opt)
を,
区切りで複数書くことが出来ます。
追加される構文の記法
なにかしら(opt)
省略可能(例だと、「なにかしら」を省略可能)
宣言子
識別子 初期化子(opt)
宣言子 ,
識別子 初期化子(opt)
識別子
識別子は、変数の名前になるものです。以下に挙げるルールを守る必要があります。
ただし、MSVCは独自拡張として、あらゆる文字を使用することが出来ます。
識別子のルール
使える文字
基本ソース文字集合のアルファベット、数字、_
など
使えない単語
キーワードとなる単語
使ってはいけないアンダーバー1
識別子中に__
(アンダーバーが2つ連続すること)
使ってはいけないアンダーバー2
識別子先頭に_
と大文字のアルファベット
使ってはいけない場所があるアンダーバー
識別子先頭に_
キーワード
キーワードは、C++規格で特別な意味を持っているトークンのことです。
組み込み型の型名や修飾子等など(int
、double
など)がこれにあたります。
なお、Visual Studioでは、キーワードは青く表示されます。
一覧はC++ のキーワード - cppreference.comを参照してください。
整数を扱う型
整数を扱う型は、組み込み型の1つであるint
です。
signed/unsignedで符号のあり/なし、即ち負の数が扱えない/るを、
short/long/long longで変数のサイズを指定することが出来ます。
サイズについては、次回に整数の内部表現を解説すると同時に解説します。
主な整数を扱う型と別名
short int
short/signed short/signed short int
int
signed/signed int
long int
long/signed long/signed long int
long long int
long long/signed long long/signed long long int
unsigned short int
unsigned short
unsigned int
unsigned
unsigned long int
unsigned long
unsigned long long int
unsigned long long
初期化
宣言すると同時に、値を設定しましょう。それが初期化です。
大雑把に説明すると、下3つは、値を省略すると0
で初期化され、
特に、下2つは、値が変数で表せない値であるかのチェックがされます。
初期化子
=
値
(
値(opt) )
{
値(opt) }
=
{
値(opt) }
初期化してない組み込み型
初期化をしなかった組み込み型の変数は、その持つ値は不定になります。
この不定の値を持つ組み込み型の変数は、その値を使うことはしてはいけません。
その様な変数を使って良い状態になるのは、代入等で値を設定してからです。
コンパイルエラーは出ませんが、標準規格で未定義動作とされる状態になります。
未定義動作
未定義動作とは、C++の規格が処理系に対していかなる制約も課さない動作のことです。
未定義動作に陥ると、正真正銘何が起こるかわからず、ほとんどバグに繋がります。
特に、タイムトラベルしたり鼻から悪魔が出るので、起こしてはいけません。
代入
初期化したしないに関わらず、宣言後に値を変更するときは代入式を使用します。
左辺には変数、演算子は=
、右辺には値(変数も大丈夫)を書きます。
すると、右辺の値を左辺に設定する働きをします。数学的なイコールとは違います。
代入は式なので、最後に;
を付けて文にしないと存在できないことに注意しましょう。
構文は以下のようになります。2つ目の挙動は初期化のものとほぼ同じです。
代入
変数 =
値
変数 =
{
値(opt) }
小数を扱おう
次は小数を扱ってみましょう。次のコードをコピペして実行しましょう。
小数
#include <iostream>
#include <iomanip>
int main()
{
float f;
double d = .1;
long double ld = 1234e-5;
f = 2.345;
std::cout << std::fixed << std::setprecision(10)
<< "f : " << f << "\n"
<< "d : " << d << "\n"
<< "ld : " << ld << "\n\n";
}
実行結果例
f : 2.3450000286
d : 0.1000000000
ld : 0.0123400000
実行結果例補足
代入値と異なる表示になる場合があります。
が、それは生じてしまう誤差なので気にしない方針にしてください。
解説
コードの解説をします。
概説
小数を扱える型は、float
とdouble
とlong double
があります。
これらは、左よりは右の方がより誤差無く表せる値の範囲が大きい、又は同様になります。
ただし、宣言と初期化、代入についての文法は、整数と同様なので飛ばします。
もちろん、整数と同様に、初期化をしないと不定な値を持ち、それを使用してはいけません。
小数リテラル
前回軽く取り上げた小数リテラルですが、指数表記も用いることが出来ます。
指数表記は、1.2e5
や-1.2e-5
など、仮数×基数の指数乗で表現する表記法です。
普通1.2×10**5
などと基数を書きますが、C++ではE
やe
を10
として用いて表記します。
また通常の表記法でも、整数部分や小数部分が0の場合、0を省略することも出来ます。
なお、**
は冪乗のことを表すとします。例えば、10**5
は100000
になります。
std::coutとマニピュレータ
std::cout
、実は出力を少し変化させることが出来、その時に使うのがマニピュレータです。
マニピュレータを使う時には、main関数の前に#include <iomanip>
と書きます。
小数を表示するときには、std::fixed
とstd::setprecision
が便利です。
先にstd::fixed
を出力し、次にstd::setprecision(自然数)
として出力します。
すると、小数点以下の出力桁数がstd::setprecision
の括弧の中に書いた値になります。
文字列を扱おう
ひとまず最後に文字列を扱いましょう。
文字列
#include <iostream>
#include <string>
int main()
{
std::string s1 = "namanegi",
s2("namagoe"),
s3{ "namahatsune" },
s4 = { "mikudayo-" };
std::cout << "s1 : " << s1
<< "\ns2 : " << s2
<< "\ns3 : " << s3
<< "\ns4 : " << s4 << "\n";
}
実行結果
s1 : namanegi
s2 : namagoe
s3 : namahatsune
s4 : mikudayo-
解説
それでは解説です。
文字の表現
例によってコンピューターでは文字を直接表現することは出来ません。
なので、文字1つ1つに一意な番号をつけ、それを符号化することで表現しています。
また、文字とその番号、そしてそれの符号化形式を定めたものを文字コードと呼びます。
なお、符号化は、あるデータをコンピューターで表せるような形式にすることを指します。
文字列の表現
文字列は、当然文字の列ですから、変数で扱うときは1つの整数に収まる筈はありません。
沢山変数を並べた物を1つの変数として扱う必要があり、それを実現する機能が配列です。
文字列は配列を使って表現されますが、難しいので標準ライブラリを使いましょう。
<string>とstd::string
使う標準ライブラリはstring
です。#include <string>
をmain関数の前に書きましょう。
文字列を扱うために作られた、std::string
という型が使用できるようになります。
std::stirng
は、ユーザー定義型ですから、型の定義をstring
から導入しないといけません。
組み込み型と違う所
宣言と代入は組み込み型と同様ですが、初期化はユーザー定義型であるので、少し異なります。
丸括弧や波括弧を使った初期化の場合、,
区切りの値の並びを取ることがあります。
ただし、これは型での定義次第なので、特になにも取らないときもあります。
詳しくは、ユーザー定義型の定義方法の1つ、クラスを扱うときに同時に扱います。
入力
さて、変数をやったので、実行時に値を決める手頃な手段の入力をしましょう。
標準出力の逆で、標準入力から値を取得しましょう。
入力
#include <iostream>
#include <string>
int main()
{
int i;
double d;
std::string s, t;
std::cin >> i >> d >> s >> t;
std::cout << "i : " << i
<< "\nd : " << d
<< "\nts: " << t << s;
}
入力例
今度は、コンソールに入力する必要があるので、入力も含めて実行結果例を書きます。
MSVCでは、標準では1つのコンソールに入出力をするので、混ざることがあります。
また、今後は入力に$
を先頭に追加して表記していきます。
実行結果例
$ 10 0.34 dayo- miku
i : 10
d : 0.34
ts: mikudayo-
実行結果例
$ 10
$ 0.34
$ dayo-
$ miku
i : 10
d : 0.34
ts: mikudayo-
解説
もう少し面白くなってきたと思います。
標準入力とstd::cin
標準出力の逆です。コンソールから数字や文字列を入力することが出来ます。
std::cin
から入力する時に使う演算子も逆で、右シフト演算子である、>>
を使用します。
変数s
とt
のように、文字列の場合は半角スペースや改行があると区切られて入力されます。
練習問題
今回はAPG4bという、AtCoder社のC++教材の問題も解いてみましょう。
AtCoder社は、プログラミングで問題を解くオンラインゲームを提供しています。
詳しくは、AtCoderとは - 競技プログラミング講習 - 駒場東邦物理部を参照してください。
AtCoderへの登録を済ませていた場合、AtCoderの問題ページ下から提出が出来ます。
GCCを選んで提出し、ACと出れば正解、CEならコンパイルエラー、WAなら不正解です。
その1-問題文
空白区切りの文字列S
,T
の入力に対して、T
、S
の順でくっつけて出力してください。
入力例は便宜的にまとめています。空行の上と下でそれぞれ入力、出力だと思って下さい。
入力例と出力例
$ oumu watashiha
watashihaoumu
回答例
回答例
#include <iostream>
#include <string>
int main()
{
std::string s;
std::string t;
std::cin >> s >> t;
std::cout << t << s;
}
その2-問題文
2つの整数A
,B
が以下のように与えられます。A+B
の結果と改行を出力してください。
出典:EX5 - A足すB問題
回答例
回答例
#include <iostream>
int main()
{
int a;
int b;
std::cin >> a >> b;
std::cout << a + b << "\n";
}
文字コード物語
文字を表すのは大変なんだ、という話をします。
この段落は私情が入った言い回しをしていますがお察しください。
ASCII
まずほとんどの符号化形式の元となっているのが、ASCIIと呼ばれる文字コードです。
ASCIIでは、0-127
の範囲に基本的な文字を割り当て、それを7bit以上の整数で符号化します。
実際の対応表は、ASCII - Wikipediaを確認してください。
悪夢の始まり
ASCIIの成立後、ASCIIが使用しない範囲に文字を割り当てた文字コードが乱立しました。
地域ごとに文字コードが存在し、文字コードを切り替えたりする必要があるのです。
これは、データの置き場たるメモリが、今のPC程潤沢でないことが理由していました。
日本語Windowsでは現在に至るまで、Shift-JISの亜種Windows-31Jが使われています。
悪夢の終わりかのように見えた
そうして乱立した文字コードの波に飲まれ、疲弊してしていたプログラマ達[要検証]。
そこに現れたのは、世界中の全ての文字を一つの文字コードにしようとしたUnicode。
Unicodeは元々、世界の文字を16bitに収めようとして作られ、様々模索されました。
しかし、世界の文字が16bitに収まる筈もなく、それらの模索は負債と化しました。
現在
さて、Unicodeが普及し、事無き事を得たように思えますが、そうでもありません。
現在のUnicodeでは、UTF-8、UTF-16、UTF-32という符号化形式が使われていますが、
日本語Windowsでは、未だにWindows-31Jが標準で、UTF-8のサポートはまだまだです。
C+においても、Unicode対応が杜撰であることは広く知られています。
互換性等様々な壁があることは確かなので、一概に言える話では無いのですが。
参照、出典
参照と出典です。
参照
オブジェクト - cppreference.com
https://ja.cppreference.com/w/cpp/language/object
型 - cppreference.com
https://ja.cppreference.com/w/cpp/language/type
組み込み型 (C++) | Microsoft Docs
https://docs.microsoft.com/ja-jp/cpp/cpp/fundamental-types-cpp?view=msvc-160
宣言 - cppreference.com
https://ja.cppreference.com/w/cpp/language/declarations
識別子 - cppreference.com
https://ja.cppreference.com/w/cpp/language/identifiers
C++ 識別子 | Microsoft Docs
https://docs.microsoft.com/ja-jp/cpp/cpp/identifiers-cpp?view=msvc-160
C++ のキーワード - cppreference.com
https://ja.cppreference.com/w/cpp/keyword
C++ キーワード | Microsoft Docs
https://docs.microsoft.com/ja-jp/cpp/cpp/keywords-cpp?view=msvc-160
初期化 - cppreference.com
https://ja.cppreference.com/w/cpp/language/initialization
代入演算子 - cppreference.com
https://ja.cppreference.com/w/cpp/language/operator_assignment
文字コード - Wikipedia
https://ja.wikipedia.org/wiki/%E6%96%87%E5%AD%97%E3%82%B3%E3%83%BC%E3%83%89
std::basic_string - cppreference.com
https://ja.cppreference.com/w/cpp/string/basic_string
std::cin, std::wcin - cppreference.com
https://ja.cppreference.com/w/cpp/io/cin
C++標準化委員会、ついに文字とは何かを理解する: char8_t - Qiita
https://qiita.com/yumetodo/items/54e1a8230dbf513ea85b
ASCII - Wikipedia
https://ja.wikipedia.org/wiki/ASCII
Unicode - Wikipedia
https://ja.wikipedia.org/wiki/Unicode
Shift_JIS - Wikipedia
https://ja.wikipedia.org/wiki/Shift_JIS
Microsoftコードページ932 - Wikipedia
https://ja.wikipedia.org/wiki/
Microsoft%E3%82%B3%E3%83%BC%E3%83%89%E3%83%9A%E3%83%BC%E3%82%B8932